package kit;

import gui.TablatureWindow;
import java.io.Serializable;
import midi.MidiObserver;
import tablature.BarLine;
import tablature.Tablature;

/**
 * Class: Component.java
 * Description: Component is an abstract class that all sub-components
 * (DrumComponent, CymbalComponent), must extend. Component is also
 * considered an Observer to the MidiController. When MidiController
 * sends out a message signaling a hit, it sends the message to
 * update(int assignment) and Component determines if this is
 * the Component that is cared for.
 * Component is Serializable because it is going to be stored in
 * the Java Preferences later. See data/UserPreferences.java
 *
 * @author et
 */

public class Component implements MidiObserver, RequiredSettings, OptionalSettings, Serializable
{


	protected int assignment;
	protected String name;
	protected String shortName;
    protected char regHit;


    // These following fields are optional
    protected boolean lowVelocityDetection;
    protected boolean highVelocityDetection;

    protected int lowVelocity;
    protected int highVelocity;

    protected char lowHit;
    protected char highHit;

    protected boolean flamDetection;
    protected boolean chokeDetection;

    protected long flamTime;
    protected long chokeTime;

    protected char flamHit;
    protected char chokeHit;



    /**
     * Component constructor.
     * @param assignment - the MIDI id (key number)
     * @param name - the name for this Component (Snare, Ride, etc.)
     * @param shortName - the short name for this Component, used in the
     *                    tablature (S, Rd, etc.)
     */
	protected Component(int assignment, String name, String shortName, char regHit)
	{
		this.assignment = assignment;
		this.name = name;
		this.shortName = shortName;
        this.regHit = regHit;


        setOptionalSettings();
	}

    public Component(Component c)
    {
        this.assignment = c.getAssignment();
        this.name = c.getName();
        this.shortName = c.getShortName();
        this.regHit = c.getRegHit();
        this.lowVelocityDetection = c.hasLowVelocityDetection();
        this.highVelocityDetection = c.hasHighVelocityDetection();
        this.lowVelocity = c.getLowVelocity();
        this.highVelocity = c.getHighVelocity();
        this.lowHit = c.getLowHit();
        this.highHit = c.getHighHit();
        this.flamDetection = c.hasFlamDetection();
        this.chokeDetection = c.hasChokeDetection();
        this.chokeTime = c.getChokeTime();
        this.flamTime = c.getFlamTime();
        this.flamHit = c.getFlamHit();
        this.chokeHit = c.getChokeHit();
    }


    /**
     * Gets the assignment for this component.
     * @return assignment - the assignment for this component.
     */
	public int getAssignment()
    {
		return assignment;
	}

    /**
     * Set the assignment for this component.
     * @param assignment - the new assignment
     */
	public void setAssignment(int assignment)
    {
		this.assignment = assignment;
	}

    /**
     * Gets the name for this component.
     * @return name - the name of this component.
     */
	public String getName()
    {
		return name;
	}

    /**
     * Sets the name for this component.
     * @param name - the new name.
     */
	public void setName(String name)
    {
		this.name = name;
	}

    /**
     * Gets the short name of this component.
     * @return shortName - the short name of this component.
     */
	public String getShortName()
    {
		return shortName;
	}

    /**
     * Sets the short name for this component.
     * @param shortName - the new short name.
     */
	public void setShortName(String shortName)
    {
		this.shortName = shortName;
	}

    /**
     * Gets the regular hit for this component.
     * @return regHit - the regular hit for this component.
     */
    public char getRegHit()
    {
        return regHit;
    }

    /**
     * Sets the regular hit for this component.
     * @param regHit - the new regular hit.
     */
    public void setRegHit(char regHit)
    {
        this.regHit = regHit;
    }


    /**
     * Abstract method that sub classes must implement.
     * They choose what optional settings they want.
     */
    protected void setOptionalSettings()
    {
        
    }

    /**
     * Gets whether this component has low velocity detection
     * @return lowVelocityDetection - True if it has low velocity detection, False otherwise.
     */
    public boolean hasLowVelocityDetection()
    {
        return lowVelocityDetection;
    }

    /**
     * Sets whether this component has low velocity detection.
     * @param lowVelocityDetection - whether or not the component has low velocity detection.
     */
    public void setLowVelocityDetection(boolean lowVelocityDetection)
    {
        this.lowVelocityDetection = lowVelocityDetection;
    }

    /**
     * Gets whether this component has high velocity detection
     * @return highVelocityDetection - True if it has high velocity detection, false otherwise.
     */
    public boolean hasHighVelocityDetection()
    {
        return highVelocityDetection;
    }

    /**
     * Sets whether this component has high velocity detection.
     * @param highVelocityDetection - whether or not the component has high velocity detection.
     */
    public void setHighVelocityDetection(boolean highVelocityDetection)
    {
        this.highVelocityDetection = highVelocityDetection;
    }

    /**
     * Gets the low velocity.
     * @return lowVelocity - the low velocity value.
     */
    public int getLowVelocity()
    {
        return lowVelocity;
    }

    /**
     * Sets the low velocity
     * @param lowVelocity - the new low velocity value.
     */
    public void setLowVelocity(int lowVelocity)
    {
        this.lowVelocity = lowVelocity;
    }

    /**
     * Gets the high velocity.
     * @return highVelocity - the high velocity value.
     */
    public int getHighVelocity()
    {
        return highVelocity;
    }

    /**
     * Sets the high velocity
     * @param highVelocity - the new high velocity value.
     */
    public void setHighVelocity(int highVelocity)
    {
        this.highVelocity = highVelocity;
    }

    /**
     * Gets the low hit.
     * @return lowHit - the low hit for this component.
     */
    public char getLowHit()
    {
        return lowHit;
    }

    /**
     * Sets the low hit.
     * @param lowHit - the new low hit.
     */
    public void setLowHit(char lowHit)
    {
        this.lowHit = lowHit;
    }

    /**
     * Gets the high hit.
     * @return highHit - the high hit for this component.
     */
    public char getHighHit()
    {
        return highHit;
    }

    /**
     * Sets the high hit.
     * @param highHit - the new high hit.
     */
    public void setHighHit(char highHit)
    {
        this.highHit = highHit;
    }

    /**
     * Gets whether this component has flam detection
     * @return flamDetection - True if the component has flam detection, false otherwise.
     */
    public boolean hasFlamDetection()
    {
        return flamDetection;
    }

    /**
     * Sets whether this component has flam detection
     * @param flamDetection - whether of not this component has flam detection.
     */
    public void setFlamDetection(boolean flamDetection)
    {
        this.flamDetection = flamDetection;
    }

    /**
     * Gets whether this component has choke detection
     * @return chokeDetection - True if the component has choke detection, false otherwise.
     */
    public boolean hasChokeDetection()
    {
        return chokeDetection;
    }

    /**
     * Sets whether this component has choke detection
     * @param chokeDetection - whether of not this component has choke detection.
     */
    public void setChokeDetection(boolean chokeDetection)
    {
        this.chokeDetection = chokeDetection;
    }

    /**
     * Gets the flam time.
     * @return flamTime - the flam time.
     */
    public long getFlamTime()
    {
        return flamTime;
    }

    /**
     * Sets the flam time.
     * @param flamTime - the new flam time.
     */
    public void setFlamTime(long flamTime)
    {
        this.flamTime = flamTime;
    }

    /**
     * Gets the choke time.
     * @return chokeTime - the choke time.
     */
    public long getChokeTime()
    {
        return chokeTime;
    }

    /**
     * Sets the choke time.
     * @param chokeTime - the new choke time.
     */
    public void setChokeTime(long chokeTime)
    {
        this.chokeTime = chokeTime;
    }

    /**
     * Sets the flam hit.
     * @param flamHit - the new flam hit.
     */
    public void setFlamHit(char flamHit)
    {
        this.flamHit = flamHit;
    }

    /**
     * Gets the flam hit.
     * @return flamHit - the flam hit.
     */
    public char getFlamHit()
    {
        return flamHit;
    }

    /**
     * Sets the choke hit.
     * @param chokeHit - the new choke hit.
     */
    public void setChokeHit(char chokeHit)
    {
        this.chokeHit = chokeHit;
    }

    /**
     * Gets the choke hit.
     * @return chokeHit - the choke hit.
     */
    public char getChokeHit()
    {
        return chokeHit;
    }




    private boolean hitOn = false;

    private synchronized boolean getHitOn()
    {
        return hitOn;
    }

    private synchronized void setHitOn(boolean hitOn)
    {
        this.hitOn = hitOn;
    }




    /**
     * What to do when receiving a note on message from
     * the controller.
     * @param assignment - the assignment that was hit.
     * @param velocity - the velocity at which the component was hit.
     */
    public void hitOn(int assignment, int velocity)
    {
        if (this.assignment == assignment)
        {
            char hit = regHit;

            /*
            if (flamDetection)
            {

            }
            else if (chokeDetection)
            {

            }
            */
            if (lowVelocityDetection && velocity < lowVelocity + 5)
            {
                hit = lowHit;
            }
            else if (highVelocityDetection && velocity > highVelocity - 5)
            {
                hit = highHit;
            }

            (new Thread(new HitThread(hit))).start();

        }
    }


    class HitThread implements Runnable
    {
        char hit = BarLine.REST;
        public HitThread(char hit)
        {
            this.hit = hit;
        }

        public void run()
        {
            Tablature.getInstance().hit(shortName, hit);
        }

    }


    /**
     * What to do when receiving a note off message from
     * the controller.
     * @param assignment - the assignment that was hit.
     * @param velocity - the velocity at which the component was hit.
     */
    public void hitOff(int assignment, int velocity)
    {

    }

    /**
     * What to do when receiving a polyphonic touch message from
     * the controller.
     * @param assignment - the assignment that was hit.
     * @param velocity - the velocity at which the component was hit.
     */
    public void touch (int assignment, int velocity)
    {
        
    }


    /**
     * Gets the type for this component.
     * @return just the name.
     */
    public String toString()
    {
        return name;
    }


}
